package com.yx;

import javax.servlet.Servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Tomcat {
    //保存tomcat中有哪些应用，每个应用有哪些servlet。使用map进行保存。   思路:tomcat->app->servlet
    private Map<String,Context> contextMap=new HashMap<>();
    //启动方法:创建socket连接，监听端口，返回socket对象
    public void start(){
    //Socket连接 TCP ，socket通信
        try{
            //线程池管理 创建了20个线程池
            ExecutorService executorService= Executors.newFixedThreadPool(20);
            //使用serverSocket创建了socket连接，监听8080端口。
            ServerSocket serverSocket=new ServerSocket(8080);
            //true的原因是为了监听socket连接，当有连接时，会返回一个socket对象，然后交给线程池处理
            while(true) {
                //调用serverSocket的accept方法，返回一个socket对象。
                Socket socket = serverSocket.accept();
                //线程池处理socket连接.这里使用了SocketProcessor的构造函数。编写一个socketProcessor类
                executorService.execute(new SocketProcessor(socket,this));
                //这里还没有调用SocketProcessor的run方法【不知道需不需要调用】
            }
        }catch (IOException e)
        {
            //这里需要抛出异常，因为socket连接会抛出异常。
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) {
        //主程序创建tomcat类，并且调用tomcat其中的方法
    Tomcat tomcat=new Tomcat();
        //部署方法
    tomcat.deployApps();
    //启动方法
    tomcat.start();
    }
    //部署方法

    private void deployApps() {
        //获取webapps目录下的文件夹。
        File webapps=new File(System.getProperty("user.dir"),"webapps");
        //遍历webapps下的文件夹进行部署
        for(String app:webapps.list()){
                deployApp(webapps,app);
        }
    }
    //部署方法，参数为webapps和appName，appName为webapps下的文件夹名称。
    private void deployApp(File webapps,String appName) {
        //这里使用了context类，context类中有一个urlPatternMapping，保存了servlet。
        Context context=new Context(appName);
        //获取项目下的servlet
        File appDirectory = new File(webapps,appName);
        //获取项目下的classes文件夹
        File classesDirectory = new File(appDirectory, "classes");
        //获取项目下的classes文件夹下的所有文件。
        List<File> files=getAllFilePath(classesDirectory);
        //遍历classes文件夹下的所有文件，判断是否是servlet。
        for(File clazz:files){
            String name=clazz.getPath();
            name=name.replace(classesDirectory.getPath()+"\\","");
            name=name.replace(".class","");
            name=name.replace("\\",".");
            //到上述步骤中，获取了servlet的类名。因为路径是使用'\',对文件进行格式化处理，使用'.'分割
            try{
                //创建一个类加载器，加载classes文件夹下的所有文件。
                WebappClassLoader classLoader=new WebappClassLoader(new URL[]{classesDirectory.toURL()});
                //加载类
                Class<?> servletClass=classLoader.loadClass(name);
                //判断继承的类是不是httpservlet。
                if(HttpServlet.class.isAssignableFrom(servletClass)){
                    if(servletClass.isAnnotationPresent(WebServlet.class)){
                        WebServlet annotation=servletClass.getAnnotation(WebServlet.class);
                        String[] urlPatterns=annotation.urlPatterns();
                        for(String urlPattern:urlPatterns){
                            //调用context的方法，将servlet添加到context中。
                            context.addUrlPatternMapping(urlPattern,(Servlet)servletClass.newInstance());
                        }
                    }
                }
                //用来打印servlet的类名。
                System.out.println(servletClass);
            }catch (ClassNotFoundException | MalformedURLException | InstantiationException | IllegalAccessException e)
            {
                //抛出异常
                throw new RuntimeException(e);
            }
        }
        //保存context到contextMap中。添加映射
        contextMap.put(appName,context);
        System.out.println("Deployed app: " + appName + " with " + context.urlPatternMapping.size() + " servlets.");
    }
    //获取项目下的所有文件
    public List<File> getAllFilePath(File srcFile){
        List<File> result=new ArrayList<>();
        File[] files=srcFile.listFiles();
        if(files!=null){
            for(File file:files){
                if(file.isDirectory()){
                    //递归调用，获取文件夹下的所有文件。
                    result.addAll(getAllFilePath(file));
                }else{
                    result.add(file);
                }
            }
        }
        return result;
    }
    //获取contextMap
    public Map<String,Context> getContextMap(){
        return contextMap;
    }
}


//问题1：Request中getRequestURI方法返回值为String ,实际应该为StringBuffer。所以SocketProcessor中将
//String requestUrl=request.getRequestURI();返回值为空。
//问题2：SocketProcessor中的servlet.service(request,response);一直返回为空。
//可能是因为Context中getByUrlPattern方法返回值为null。
//原因：yxservlet中的@WebServlet注解中@WebServlet(urlPatterns = {"/yx"})没有指定urlPatterns。
//将urlPatterns加上后显示Deployed app: hello with 1 servlets.存在一个servlets。
